﻿#include <jni.h>
#include <string.h>
#include <stdio.h>

#define TAG "myDemo-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型

extern "C" {

const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
char *jstringToChar(JNIEnv *env, jstring jstr);
jstring charToJstring(JNIEnv *env, const char *pat);

void Java_com_ijustyce_weekly1601_jni_JniUtils_checkSign(JNIEnv *env, jobject thiz, jobject context);

};

//jstring to char*
char *jstringToChar(JNIEnv *env, jstring jstr) {
    char *rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes",
                                     "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char *) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;

}

//char* to jstring
jstring charToJstring(JNIEnv *env, const char *pat) {
    jclass strClass = env->FindClass("java/lang/String");
    jmethodID ctorID = env->GetMethodID(strClass, "<init>",
                                        "([BLjava/lang/String;)V");
    jbyteArray bytes = env->NewByteArray(strlen(pat));
    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *) pat);
    jstring encoding = env->NewStringUTF("utf-8");
    return (jstring) env->NewObject(strClass, ctorID, bytes, encoding);
}

/* */
char *base64_encode(const char *data) {
    int data_len = strlen(data);
    int prepare = 0;
    int ret_len;
    int temp = 0;
    char *ret = NULL;
    char *f = NULL;
    int tmp = 0;
    char changed[4];
    int i = 0;
    ret_len = data_len / 3;
    temp = data_len % 3;
    if (temp > 0) {
        ret_len += 1;
    }
    ret_len = ret_len * 4 + 1;
    ret = (char *) malloc(ret_len);

    if (ret == NULL) {
        return 0;
    }
    memset(ret, 0, ret_len);
    f = ret;
    while (tmp < data_len) {
        temp = 0;
        prepare = 0;
        memset(changed, '\0', 4);
        while (temp < 3) {
            //printf("tmp = %d\n", tmp);
            if (tmp >= data_len) {
                break;
            }
            prepare = ((prepare << 8) | (data[tmp] & 0xFF));
            tmp++;
            temp++;
        }
        prepare = (prepare << ((3 - temp) * 8));
        //printf("before for : temp = %d, prepare = %d\n", temp, prepare);
        for (i = 0; i < 4; i++) {
            if (temp < i) {
                changed[i] = 0x40;
            } else {
                changed[i] = (prepare >> ((3 - i) * 6)) & 0x3F;
            }
            *f = base[changed[i]];
            //printf("%.2X", changed[i]);
            f++;
        }
    }
    *f = '\0';
    return ret;
}

/* */
static char find_pos(char ch) {
    char *ptr = (char *) strrchr(base, ch);
    return (ptr - base);
}

/* */
char *base64_decode(const char *data) {
    int data_len = strlen(data);
    int ret_len = (data_len / 4) * 3;
    int equal_count = 0;
    char *ret = NULL;
    char *f = NULL;
    int tmp = 0;
    int temp = 0;
    char need[3];
    int prepare = 0;
    int i = 0;
    if (*(data + data_len - 1) == '=') {
        equal_count += 1;
    }
    if (*(data + data_len - 2) == '=') {
        equal_count += 1;
    }
    if (*(data + data_len - 3) == '=') { //seems impossible
        equal_count += 1;
    }
    switch (equal_count) {
        case 0:
            ret_len += 4; //3 + 1 [1 for NULL]
            break;
        case 1:
            ret_len += 4; //Ceil((6*3)/8)+1
            break;
        case 2:
            ret_len += 3; //Ceil((6*2)/8)+1
            break;
        case 3:
            ret_len += 2; //Ceil((6*1)/8)+1
            break;
        default:
            break;
    }
    ret = (char *) malloc(ret_len);
    if (ret == NULL) {
        return 0;
    }
    memset(ret, 0, ret_len);
    f = ret;
    while (tmp < (data_len - equal_count)) {
        temp = 0;
        prepare = 0;
        memset(need, 0, 4);
        while (temp < 4) {
            if (tmp >= (data_len - equal_count)) {
                break;
            }
            prepare = (prepare << 6) | (find_pos(data[tmp]));
            temp++;
            tmp++;
        }
        prepare = prepare << ((4 - temp) * 6);
        for (i = 0; i < 3; i++) {
            if (i == temp) {
                break;
            }
            *f = (char) ((prepare >> ((2 - i) * 8)) & 0xFF);
            f++;
        }
    }
    *f = '\0';
    return ret;
}

void showToast(JNIEnv *env, jobject thiz, jobject txt, jint time) {

    jclass Toast = NULL;
    jobject toast = NULL;
    jmethodID makeText = NULL;
    jmethodID show = NULL;

    Toast = env->FindClass("android/widget/Toast");
    if (NULL == Toast) {
        return;
    }
    makeText =
            env->GetStaticMethodID(Toast, "makeText",
                                   "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
    if (NULL == makeText) {
        return;
    }
    toast = env->CallStaticObjectMethod(Toast, makeText, thiz, txt, time);
    if (NULL == toast) {
        return;
    }
    show = env->GetMethodID(Toast, "show", "()V");
    if (NULL == show) {
        return;
    }
    env->CallVoidMethod(toast, show);

}

void exitApp(JNIEnv *env) {

//    jstring what = env->NewStringUTF("非法App，程序即将退出！");
//    showToast(env, thiz, what, 1000);

    jclass android = env->FindClass("android/os/Process");
    jmethodID killProcess = env->GetStaticMethodID(android, "killProcess", "(I)V");
    jmethodID myId = env->GetStaticMethodID(android, "myPid", "()I");
    env->CallStaticVoidMethod(android, killProcess, env->CallStaticIntMethod(android, myId));

    jclass system = env->FindClass("java/lang/System");
    jmethodID exit = env->GetStaticMethodID(system, "exit", "(I)V");
    env->CallStaticVoidMethod(system, exit, 0);
}

//  签名校验

const char *RELEASE_SIGN = "308201dd30820146020101300d06092a864886f70d01010505003037311630140603550"
        "4030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130"
        "25553301e170d3136313031333038313332315a170d3436313030363038313332315a303731163014060355040"
        "30c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b3009060355040613025"
        "55330819f300d06092a864886f70d010101050003818d0030818902818100a76db675eae40dffa9046b7c90e89"
        "e58331d58a3358be4b8dbabb9072b742bdb822e03cf9c022c0f3bab31086c27ca92b8073c330ce7ea4950a12f0"
        "b5955c48e3c8ba12ea573cc1d373659deeec49af643b9478c1f7f86632b37ac868a630885863721f81347be8c7"
        "362de4e0ba50ae235e171142cf15980553f57a7adb2ee7b0203010001300d06092a864886f70d0101050500038"
        "1810080540453ae88f54d4be0d74ec9af00f7291934908908ee60115b0ab11c313a449a1e947e1091e5d93290a"
        "613471d612ff5aacae514efb1f3c5feb07ee1cf7c692922fb9f34ad87e6a25c2b816ff1b5d2fc1dd228c21eb48"
        "02ca8ebbf6363136c9e5c83a8f87f46896952e0d4f4f63aa145f4d5ee52890115f4c1d92155873140";

static void checkSign(JNIEnv *env, jobject object, jobject contextObject) {
    jclass native_class = env->GetObjectClass(contextObject);
    jmethodID pm_id = env->GetMethodID(native_class, "getPackageManager","()Landroid/content/pm/PackageManager;");
    jobject pm_obj = env->CallObjectMethod(contextObject, pm_id);
    jclass pm_clazz = env->GetObjectClass(pm_obj);
    // 得到 getPackageInfo 方法的 ID
    jmethodID package_info_id = env->GetMethodID(pm_clazz, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
    jclass native_classs = env->GetObjectClass(contextObject);
    jmethodID mId = env->GetMethodID(native_classs, "getPackageName","()Ljava/lang/String;");
    jstring pkg_str = static_cast<jstring>(env->CallObjectMethod(contextObject, mId));
    // 获得应用包的信息
    jobject pi_obj = env->CallObjectMethod(pm_obj, package_info_id, pkg_str, 64);
    // 获得 PackageInfo 类
    jclass pi_clazz = env->GetObjectClass(pi_obj);
    // 获得签名数组属性的 ID
    jfieldID signatures_fieldId = env->GetFieldID(pi_clazz, "signatures","[Landroid/content/pm/Signature;");
    jobject signatures_obj = env->GetObjectField(pi_obj, signatures_fieldId);
    jobjectArray signaturesArray = (jobjectArray) signatures_obj;
  //  jsize size = env->GetArrayLength(signaturesArray);
    jobject signature_obj = env->GetObjectArrayElement(signaturesArray, 0);
    jclass signature_clazz = env->GetObjectClass(signature_obj);
    jmethodID string_id = env->GetMethodID(signature_clazz, "toCharsString","()Ljava/lang/String;");
    jstring str = static_cast<jstring>(env->CallObjectMethod(signature_obj, string_id));
    char *c_msg = (char *) env->GetStringUTFChars(str, 0);
    if (strcmp(c_msg, RELEASE_SIGN) != 0) {
         exitApp(env);
    }
}

static jint add(JNIEnv *env, jobject thiz, jint a, jint b) {
    int result = a + b;
    return result;
}

static const char *classPathName = "com/ijustyce/weekly1601/jni/JniUtils";

static JNINativeMethod methods[] = {
        {"add", "(II)I", (void*)add },
};

/*
 * Register several native methods for one class.
 */
static int registerNativeMethods(JNIEnv* env, const char* className,
                                 JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}
/*
 * Register native methods for all classes we know about.
 *
 * returns JNI_TRUE on success.
 */
static int registerNatives(JNIEnv* env)
{
    if (!registerNativeMethods(env, classPathName,
                               methods, sizeof(methods) / sizeof(methods[0]))) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

static jobject getAppContext(JNIEnv* env) {
    jclass IApplication = env->FindClass("com/ijustyce/fastandroiddev3/IApplication");
    jmethodID getInstance = env->GetStaticMethodID(IApplication, "getInstance", "()Landroid/app/Application;");
    return env->CallStaticObjectMethod(IApplication, getInstance, "()I");
}

// ----------------------------------------------------------------------------
/*
 * This is called by the VM when the shared library is first loaded.
 */

typedef union {
    JNIEnv* env;
    void* venv;
} UnionJNIEnvToVoid;

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    UnionJNIEnvToVoid uenv;
    uenv.venv = NULL;
    jint result = -1;
    JNIEnv* env = NULL;
    jobject context = NULL;

    if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
        goto bail;
    }
    env = uenv.env;
    if (registerNatives(env) != JNI_TRUE) {
        goto bail;
    }

    context = getAppContext(env);
    if (context == NULL) {
        exitApp(env);
    }else {
        checkSign(env, context, context);
    }

    result = JNI_VERSION_1_6;

    bail:
    return result;
}